package com.googlecode.continuity.core.server.service;

import java.util.Date;
import java.util.List;

import org.acegisecurity.providers.encoding.PasswordEncoder;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.googlecode.continuity.core.client.ContinuityUser;
import com.googlecode.continuity.core.client.GrantedAuthority;
import com.googlecode.continuity.core.server.security.SecurityUserToken;

/**
 * UserServiceHibernate
 *
 * @author Peter Schmitt <aragos@aragos.de>
 */
public class UserServiceHibernate extends HibernateDaoSupport implements UserService {
// ------------------------------ FIELDS ------------------------------

  private PasswordEncoder passwordEncoder;

// --------------------- GETTER / SETTER METHODS ---------------------

  public PasswordEncoder getPasswordEncoder() {
    return passwordEncoder;
  }

  public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
    this.passwordEncoder = passwordEncoder;
  }

// ------------------------ INTERFACE METHODS ------------------------

// --------------------- Interface UserDetailsService ---------------------

  public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException {
    List users = getHibernateTemplate().find("FROM SecurityUserToken WHERE user.username = ?", name);

    // user names are unique
    if (users.size() != 1) {
      throw new UsernameNotFoundException("Invalid User");
    }
    return (SecurityUserToken) users.get(0);
  }

// --------------------- Interface UserService ---------------------

  public SecurityUserToken getUserById(int id) {
    SecurityUserToken user = (SecurityUserToken) getHibernateTemplate().get(SecurityUserToken.class, id);

    // TODO: throw exception when user not found
    /*if (user == null) {
      throw new UserNotFoundException("Invalid User");
    }*/
    return user;
  }

  public void storeUser(SecurityUserToken user) {
    getHibernateTemplate().saveOrUpdate(user);
  }

  public void refreshUser(SecurityUserToken user) {
    getHibernateTemplate().refresh(user);
  }

  public int getUserCount() {
    return DataAccessUtils.intResult(getHibernateTemplate().find("select count(*) from User"));
  }

  public boolean isCorrectPassword(SecurityUserToken userToken, String password) {
    return passwordEncoder.isPasswordValid(userToken.getPassword(), password, null);
  }

  public String encodePassword(String password) {
    return passwordEncoder.encodePassword(password, null);
  }

  public GrantedAuthority getAuthorityByName(String name) {
    List<GrantedAuthority> authorities =
        getHibernateTemplate().find("FROM GrantedAuthority WHERE name = ?", name);
    if (authorities == null || authorities.size() != 1) {
      return null;
    }
    return authorities.get(0);
  }

  // TODO: should this be done in registration controller?
  public void setInitialValues(SecurityUserToken userToken) {
    userToken.getUser().grantAuthority(getAuthorityByName("user"));
    userToken.setEnabled(true);
    userToken.getUser().setRegistered(new Date(System.currentTimeMillis()));
//    userToken.getUser()
    // TODO: configure expiration in config file
    // TODO: can this be solved another way?
    /*user.setExpiresOn(new Date(System.currentTimeMillis() + Integer.MAX_VALUE));
    user.setCredentialsExpireOn(new Date(System.currentTimeMillis() + Integer.MAX_VALUE));*/
  }

  public ContinuityUser getUserByName(String name) {
    List result = getHibernateTemplate().find("FROM User WHERE name = ?", name);
    if (result == null || result.size() != 1) {
      return null;
    }
    return (ContinuityUser) result.get(0);
  }
}
